package com.apes.framework.jpa.generator.service;

import com.apes.framework.jpa.generator.Implementation;
import com.apes.framework.jpa.generator.model.Sequence;
import com.apes.framework.jpa.generator.model.SequenceDateRange;
import com.apes.framework.jpa.generator.repository.SequenceDateRangeRepository;
import com.apes.framework.jpa.generator.repository.SequenceRepository;
import com.apes.framework.plugin.cache.Cache;
import com.apes.framework.rop.session.SimpleSession;
import com.apes.oap.session.RopSessionHolder;
import org.hibernate.dialect.Dialect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * 功能：
 *
 * @author xul
 * @create 2019-10-29 17:28
 */
@Component
public class SequenceManager {

    private final String CONST_SEQ_CACHE_NAME = "sequenceCache";
    private final String CONST_SEQ_DATE_RANGE_CACHE_NAME = "sequenceDateRangeCache";

    private static Dialect dialect;

    @Value("${spring.jpa.database-platform}")
    private String databasePlatform;

    @Autowired
    private Cache cache;

    @Autowired
    private SequenceRepository sequenceRepository;

    @Autowired
    private SequenceDateRangeRepository dateRangeRepository;

    public Sequence getSequence(String sequenceCode) {
        Sequence sequence = null;
        String id;
        Long companyId = getSessionCompanyId();
        if (companyId != null) {
            id = getKey(sequenceCode, companyId.toString());
            sequence = (Sequence) cache.get(CONST_SEQ_CACHE_NAME, id);
            if (sequence == null) {
                sequence = sequenceRepository.findFirstByCodeAndCompanyOrderByCompanyAscIdAsc(sequenceCode, companyId);
                if (sequence == null) {
                    cache.put(CONST_SEQ_CACHE_NAME, id, new Sequence());
                } else if (sequence != null && isSupportsSequences(sequence)) {
                    cache.put(CONST_SEQ_CACHE_NAME, id, sequence);
                }
            } else if (sequence.getCode() == null) {
                sequence = null;
            }
        }
        if (sequence == null) {
            id = getKey(sequenceCode, "*");
            sequence = (Sequence) cache.get(CONST_SEQ_CACHE_NAME, id);
            if (sequence == null) {
                sequence = sequenceRepository.findFirstByCodeOrderByCodeAsc(sequenceCode);
                if (sequence != null && isSupportsSequences(sequence)) {
                    cache.put(CONST_SEQ_CACHE_NAME, id, sequence);
                }
            }
        }
        return sequence;
    }

    public SequenceDateRange getSequenceDateRange(Sequence sequence) {
        SequenceDateRange sequenceDateRange;
        String id = String.valueOf(sequence.getId());
        SequenceDateRangeCache sdr = (SequenceDateRangeCache) cache.get(CONST_SEQ_DATE_RANGE_CACHE_NAME, id);
        if (sdr == null || sdr.invalid()) {
            Date date = new Date();
            sequenceDateRange = findSequenceDateRange(sequence, date);
            if (sequenceDateRange != null && isSupportsSequences(sequenceDateRange.getSequence())) {
                cache.put(CONST_SEQ_DATE_RANGE_CACHE_NAME, id, new SequenceDateRangeCache(sequenceDateRange, date));
            }
        } else {
            sequenceDateRange = sdr.getSequenceDateRange();
        }
        return sequenceDateRange;
    }

    private SequenceDateRange findSequenceDateRange(Sequence sequence, Date date) {
        return dateRangeRepository.findFirstBySequenceAndDateFromLessThanEqualAndDateToIsGreaterThanEqual(sequence, date, date);
    }

    public boolean isSupportsSequences(Sequence sequence) {
        return sequence.getImplementation().equals(Implementation.STANDARD.toString()) && getDialect().supportsSequences();
    }

    public Dialect getDialect() {
        if (dialect == null) {
            synchronized (Dialect.class) {
                try {
                    dialect = (Dialect) Class.forName(databasePlatform).newInstance();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return dialect;
    }

    private String getKey(String sequenceCode, String companyId) {
        return String.format("%s:%s", sequenceCode, companyId);
    }

    private Long getSessionCompanyId() {
        SimpleSession session = (SimpleSession) RopSessionHolder.get();
        if (session == null) return null;
        return session.getCompanyId();
    }
}
